package org.javanum.number;

import org.junit.Test;

import static org.javanum.number.TestUtils.assertComplexEquality;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
 * Copyright 2010 Scott Fines
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * <p/>
 * Author: Scott Fines
 * Date: Mar 5, 2010
 */
public class ComplexMathTestSpecialValues {
    private static final double DELTA = Math.pow(1.0,-10);
    

    /*Sin tests */

    @Test(expected=ArithmeticException.class)
    public void testSinCartesianOfInfinityFails() throws Exception{
        ComplexMath.sin(Complex.COMPLEX_INFINITY);
    }

    @Test(expected=ArithmeticException.class)
    public void testSinPolarOfInfinityFails() throws Exception{
        ComplexMath.sin(PolarComplex.COMPLEX_INFINITY);
    }

    @Test
    public void testSinOfNaNisNaNCartesian() throws Exception {
        /*
        Tests that sin of Complex.NaN is NaN
         */
        Complex sin = ComplexMath.sin(Complex.NaN);

        assertComplexEquality("sin(NaN)!=NaN",Complex.NaN,sin,DELTA);
    }

    @Test
    public void testSinOfNaNisNaNPolar() throws Exception {
        /*
        Tests that sin of Complex.NaN is NaN
         */
        PolarComplex sin = ComplexMath.sin(PolarComplex.NaN);

        assertComplexEquality("sin(NaN)!=NaN",PolarComplex.NaN,sin,DELTA);
    }


    /*-------------------------------------------------------------------*/

    /*Arcsin tests*/

    @Test
    public void testArcSinCartesianSpecialValues() throws Exception{
        //TODO -sf- implement!
    }

    @Test
    public void testArcSinPolarSpecialValues() throws Exception{
        //TODO -sf- implement!
    }

/*-------------------------------------------------------------------*/

    /*Csc tests*/

    @Test(expected=ArithmeticException.class)
    public void testCscCartesianOfInfinityFails() throws Exception{
        ComplexMath.csc(Complex.COMPLEX_INFINITY);
    }

    @Test(expected=ArithmeticException.class)
    public void testCscOfZeroFailsCartestian() throws Exception {
        /*
        csc(z) is undefined wherever sin(z)=0; therefore,
        csc(z) is undefined for z = 0 +/- pik, for k an integer.
        However, only two of those values are truly unique(all
        others should repeat the same values as these two), so only
        need to test 0 and pi.

        This method tests zero
         */
        ComplexMath.csc(Complex.ZERO);
    }

    @Test(expected=ArithmeticException.class)
    public void testCscOfPiFailsCartestian() throws Exception {
        /*
        csc(z) is undefined wherever sin(z)=0; therefore,
        csc(z) is undefined for z = 0 +/- pik, for k an integer.
        However, only two of those values are truly unique(all
        others should repeat the same values as these two), so only
        need to test 0 and pi.

        This method tests pi
         */
        ComplexMath.csc(Complex.valueOf(Math.PI,0));
    }

    @Test(expected=ArithmeticException.class)
    public void testCscOfInfinityFailsPolar() throws Exception{
        ComplexMath.csc(PolarComplex.COMPLEX_INFINITY);
    }

    @Test(expected=ArithmeticException.class)
    public void testCscOfZeroFailsPolar() throws Exception {
        /*
        csc(z) is undefined wherever sin(z)=0; therefore,
        csc(z) is undefined for z = 0+/-pi*k, for k an integer.
        However, only two of those values are truly unique(all
        others should repeat the same values as these two), so only
        need to test 0 and pi.

        This method tests zero
         */
        ComplexMath.csc(PolarComplex.ZERO);
    }

    @Test(expected=ArithmeticException.class)
    public void testCscOfPiFailsPolar() throws Exception {
        /*
        csc(z) is undefined wherever sin(z)=0; therefore,
        csc(z) is undefined for z = 0 +/- pik, for k an integer.
        However, only two of those values are truly unique(all
        others should repeat the same values as these two), so only
        need to test 0 and pi.

        This method tests pi
         */
        ComplexMath.csc(Complex.valueOf(Math.PI,0));
    }

/*-------------------------------------------------------------------*/

    /*Cos tests*/

    @Test(expected=ArithmeticException.class)
    public void testCosOfInfinityFailsCartesian() throws Exception {
        ComplexMath.cos(Complex.COMPLEX_INFINITY);
    }

    @Test(expected=ArithmeticException.class)
    public void testCosOfInfinityFailsPolar() throws Exception {
        ComplexMath.cos(PolarComplex.COMPLEX_INFINITY);
    }

/*-------------------------------------------------------------------*/

    /*Arccos tests*/

    @Test
    public void testArcCosCartesianSpecialValues() throws Exception {
        //TODO -sf- implement!
    }

    @Test
    public void testArcCosPolarSpecialValues() throws Exception {
        //TODO -sf- implement!
    }

/*-------------------------------------------------------------------*/

    /*Sec tests*/

    @Test(expected=ArithmeticException.class)
    public void testSecOfPiOver2FailsCartestian() throws Exception {
        /*
        sec(z) is undefined wherever cos(z)=0; therefore,
        sec(z) is undefined for z = pi/2 +/- pik, for k an integer.
        However, only two of those values are truly unique(all
        others should repeat the same values as these two), so only
        need to test pi/2 and 3*pi/2.

        This method tests pi/2
         */
        ComplexMath.sec(Complex.valueOf(Math.PI/2,0));
    }

    @Test(expected=ArithmeticException.class)
    public void testSecOfThreePiOver2FailsCartestian() throws Exception {
        /*
        sec(z) is undefined wherever cos(z)=0; therefore,
        sec(z) is undefined for z = pi/2 +/- pik, for k an integer.
        However, only two of those values are truly unique(all
        others should repeat the same values as these two), so only
        need to test pi/2 and 3*pi/2.

        This method tests pi/2
         */
        ComplexMath.sec(Complex.valueOf(3*Math.PI/2,0));
    }

    @Test(expected=ArithmeticException.class)
    public void testSecOfInfinityFailsCartesian() throws Exception{
        ComplexMath.sec(Complex.COMPLEX_INFINITY);
    }

    @Test(expected=ArithmeticException.class)
    public void testSecOfInfinityFailsPolar() throws Exception{
        ComplexMath.sec(Complex.COMPLEX_INFINITY);
    }

    @Test(expected=ArithmeticException.class)
    public void testSecOfPiOver2FailsPolar() throws Exception {
        /*
       sec(z) is undefined wherever cos(z)=0; therefore,
       sec(z) is undefined for z = pi/2 +/- pik, for k an integer.
       However, only two of those values are truly unique(all
       others should repeat the same values as these two), so only
       need to test pi/2 and 3*pi/2.

       This method tests pi/2
        */
        ComplexMath.sec(PolarComplex.valueOf(Math.PI/2,0));
    }

    @Test(expected=ArithmeticException.class)
    public void testSecOfThreePiOver2FailsPolar() throws Exception {
        /*
       sec(z) is undefined wherever cos(z)=0; therefore,
       sec(z) is undefined for z = pi/2 +/- pik, for k an integer.
       However, only two of those values are truly unique(all
       others should repeat the same values as these two), so only
       need to test pi/2 and 3*pi/2.

       This method tests pi/2
        */
        ComplexMath.sec(PolarComplex.valueOf(3*Math.PI/2,0));
    }

/*-------------------------------------------------------------------*/

    /*Tan tests*/

    @Test(expected=ArithmeticException.class)
    public void testTanFailsAtPiOver2Cartesian() throws Exception {
        /*
        tan(z) is undefined for the values z=pi/2+k*pi for k an
        integer. However, only z=pi/2 and z=3pi/2 are unique, so
        only need to test these.

        This method tests the value pi/2
         */
        ComplexMath.tan(Complex.valueOf(Math.PI/2,0));

    }

    @Test(expected=ArithmeticException.class)
    public void testTanFailsAtThreePiOver2Cartesian() throws Exception {
        /*
        tan(z) is undefined for the values z=pi/2+k*pi for k an
        integer. However, only z=pi/2 and z=3pi/2 are unique, so
        only need to test these.

        This method tests the value 3*pi/2
         */
        ComplexMath.tan(Complex.valueOf(3*Math.PI/2,0));
    }

    @Test(expected=ArithmeticException.class)
    public void testTanOfInfinityFailsCartesian() throws Exception {
        ComplexMath.tan(Complex.COMPLEX_INFINITY);
    }

    @Test(expected=ArithmeticException.class)
    public void testTanFailsAtPiOver2Polar() throws Exception {
        /*
        tan(z) is undefined for the values z=pi/2+k*pi for k an
        integer. However, only z=pi/2 and z=3pi/2 are unique, so
        only need to test these.

        This method tests the value pi/2
         */
        ComplexMath.tan(PolarComplex.valueOf(Math.PI/2,0));

    }

    @Test(expected=ArithmeticException.class)
    public void testTanFailsAtThreePiOver2Polar() throws Exception {
        /*
        tan(z) is undefined for the values z=pi/2+k*pi for k an
        integer. However, only z=pi/2 and z=3pi/2 are unique, so
        only need to test these.

        This method tests the value 3*pi/2
         */
        ComplexMath.tan(PolarComplex.valueOf(3*Math.PI/2,0));
    }

    @Test(expected=ArithmeticException.class)
    public void testTanOfInfinityFailsPolar() throws Exception {
        ComplexMath.tan(PolarComplex.COMPLEX_INFINITY);
    }

/*-------------------------------------------------------------------*/

    /*Arctan tests*/

    @Test
    public void testArcTanSpecialValuesCartesian() throws Exception {
        //TODO -sf- implement!
    }

    @Test
    public void testArcTanPolarSpecialValues() throws Exception {
        //TODO -sf- implement!
    }

/*-------------------------------------------------------------------*/

    /*Cot tests*/

    @Test(expected=ArithmeticException.class)
    public void testCotOfZeroFailsCartestian() throws Exception {
        /*
       cot(z) is undefined wherever sin(z)=0; therefore,
       cot(z) is undefined for z = 0 +/- pik, for k an integer.
       However, only two of those values are truly unique(all
       others should repeat the same values as these two), so only
       need to test 0 and pi.

       This method tests zero
        */
        ComplexMath.cot(Complex.ZERO);
    }

    @Test(expected=ArithmeticException.class)
    public void testCotOfPiFailsCartestian() throws Exception {
       /*
       cot(z) is undefined wherever sin(z)=0; therefore,
       cot(z) is undefined for z = 0 +/- pik, for k an integer.
       However, only two of those values are truly unique(all
       others should repeat the same values as these two), so only
       need to test 0 and pi.

       This method tests pi
        */
        ComplexMath.cot(Complex.valueOf(Math.PI,0));
    }

    @Test(expected=ArithmeticException.class)
    public void testCotOfInfinityFailsCartesian() throws Exception {
        ComplexMath.cot(Complex.COMPLEX_INFINITY);
    }

    @Test(expected=ArithmeticException.class)
    public void testCotOfZeroFailsPolar() throws Exception {
        /*
       cot(z) is undefined wherever sin(z)=0; therefore,
       cot(z) is undefined for z = 0 +/- pik, for k an integer.
       However, only two of those values are truly unique(all
       others should repeat the same values as these two), so only
       need to test 0 and pi.

       This method tests zero
        */
        ComplexMath.cot(Complex.ZERO);
    }

    @Test(expected=ArithmeticException.class)
    public void testCotOfPiFailsPolar() throws Exception {
        /*
       cot(z) is undefined wherever sin(z)=0; therefore,
       cot(z) is undefined for z = 0 +/- pik, for k an integer.
       However, only two of those values are truly unique(all
       others should repeat the same values as these two), so only
       need to test 0 and pi.

       This method tests pi
        */
        ComplexMath.cot(Complex.valueOf(Math.PI,0));
    }

    @Test(expected=ArithmeticException.class)
    public void testCotOfInfinityFailsPolar() throws Exception {
        ComplexMath.cot(Complex.COMPLEX_INFINITY);
    }

/*-------------------------------------------------------------------*/

    /*Exp tests*/

    @Test
    public void testExpCartesianSpecialValues() throws Exception{

        //test Complex_Infinity
        Complex inf = Complex.COMPLEX_INFINITY;
        Complex expInf = ComplexMath.exp(inf);
        assertTrue(String.format("e^(Infinity)!=Infinity%n" +
                "e^(Infinity)=%1$s",expInf),inf.equals(expInf));

        //test zero
        Complex zero = Complex.ZERO;
        Complex expZero = ComplexMath.exp(zero);

        assertComplexEquality("e&(0)!=1",Complex.ONE,expZero,DELTA);
//        assertEquals(String.format("e^(0)!=1--real parts don't match%n" +
//                "e^(0)=%1$s",expZero),zero.real,expZero.real,DELTA);
//
//        assertEquals(String.format("e^(0)!=1--imaginary parts don't match%n" +
//                "e^(0)=%1$s",expZero),zero.imaginary,expZero.imaginary,DELTA);

    }

    @Test
    public void testExpSpecialValuesPolar() throws Exception{
        /*
        e^(Infinity)=Infinity. e^(0)=1
         */

        PolarComplex inf = PolarComplex.COMPLEX_INFINITY;
        PolarComplex expInf = ComplexMath.exp(inf);

        assertTrue(String.format("e^(Infinity)!=Infinity%n" +
                "e^(Infinity)=%1$s",expInf),inf.equals(expInf));

        PolarComplex expZero = ComplexMath.exp(PolarComplex.ZERO);

        assertComplexEquality("e^(0)!=1",PolarComplex.ONE,expZero,DELTA);
//        assertEquals(String.format("e^(0)!=1--magnitude does not match%n" +
//                "e^(0)=%1$s",expZero),1d,expZero.magnitude,DELTA);
//        assertEquals(String.format("e^(0)!=1--angle does not match%n" +
//                "e^(0)=%1$s",expZero),0d,expZero.angle,DELTA);

    }

/*-------------------------------------------------------------------*/

    /*Ln tests*/

    @Test(expected=ArithmeticException.class)
    public void testLnFailsOnBranchCutCartesian() throws Exception{
        /*
            The natural logarithm is undefined on the branch cut
            {z|z= -a+i0,for a>0 real}. As this is an infinite set,
            we will only test one such value: z = -1+i0
         */
        ComplexMath.ln(Complex.valueOf(-1,0));
    }

    @Test(expected=ArithmeticException.class)
    public void testLnOfZeroFailsCartesian() throws Exception{
        /*
            The natural logarithm at zero is undefined.
         */
        ComplexMath.ln(Complex.ZERO);
    }

    @Test
    public void testLnOfComplexInfinityCartestian() throws Exception{
        /*
        ln(Infinity)=infinity
         */
        Complex inf = Complex.COMPLEX_INFINITY;
        Complex lnInf = ComplexMath.ln(inf);

        assertTrue(String.format("ln(Infinity)!=Infinity%n." +
                "ln(Infinity)=%1$s",lnInf),inf.equals(lnInf));
    }

    @Test(expected=ArithmeticException.class)
    public void testLnFailsOnBranchCutPolar() throws Exception{
        /*
            The natural logarithm is undefined on the branch cut
            {z|z= -a+i0,for a>0 real}. As this is an infinite set,
            we will only test one such value: z = -1+i0=e^(i*pi)
         */
        ComplexMath.ln(PolarComplex.valueOf(1,Math.PI));
    }

    @Test(expected=ArithmeticException.class)
    public void testLnOfZeroFailsPolar() throws Exception{
        /*
            The natural logarithm at zero is undefined.
         */
        ComplexMath.ln(PolarComplex.ZERO);
    }

    @Test
    public void testLnOfComplexInfinityPolar() throws Exception{
        /*
        ln(Infinity)=infinity
         */
        PolarComplex inf = PolarComplex.COMPLEX_INFINITY;
        PolarComplex lnInf = ComplexMath.ln(inf);

        assertTrue(String.format("ln(Infinity)!=Infinity%n." +
                "ln(Infinity)=%1$s",lnInf),inf.equals(lnInf));
    }

/*-------------------------------------------------------------------*/

    /*Pow tests*/

    @Test
    public void testPowCartesianSpecialValues() throws Exception{
        /*
        Infinity^pow = Infinity
         */
        Complex inf = Complex.COMPLEX_INFINITY;
        Complex infPow = ComplexMath.pow(inf, 10);

        assertTrue(String.format("Infinity^pow !=Infinity%n." +
                "Infinity^pow=%1$s",infPow),inf.equals(infPow));
    }

    @Test
    public void testPowOfInfinityPolar() throws Exception{
        /*
        Infinity^pow = Infinity
         */
        PolarComplex inf = PolarComplex.COMPLEX_INFINITY;
        PolarComplex infPow = ComplexMath.pow(inf, 10);

        assertTrue(String.format("Infinity^pow !=Infinity%n." +
                "Infinity^pow=%1$s",infPow),inf.equals(infPow));
    }

/*-------------------------------------------------------------------*/

    /*Sqrt tests*/

    @Test
    public void testSqrtCartesianSpecialValues() throws Exception{
        /*
        sqrt(Infinity)=Infinity
         */
        Complex inf = Complex.COMPLEX_INFINITY;
        Complex sqrtInf = ComplexMath.sqrt(inf);

        assertTrue(String.format("sqrt(Infinity)!=Infinity%n." +
                "sqrt(Infinity)=%1$s",sqrtInf),inf.equals(sqrtInf));
    }

    @Test
    public void testSqrtOfInfinityPolar() throws Exception{
        /*
        sqrt(Infinity)=Infinity
         */
        PolarComplex inf = PolarComplex.COMPLEX_INFINITY;
        PolarComplex sqrtInf = ComplexMath.sqrt(inf);

        assertTrue(String.format("sqrt(Infinity)!=Infinity%n." +
                "sqrt(Infinity)=%1$s",sqrtInf),inf.equals(sqrtInf));
    }

/*-------------------------------------------------------------------*/

    /*All Roots tests*/

    @Test
    public void testAllRootsCartesianSpecialValues() throws Exception {
         /*
        sqrt(Infinity)=Infinity
         */
        Complex inf = Complex.COMPLEX_INFINITY;
        Complex[] sqrtInf = ComplexMath.allRoots(inf,5);

        for(Complex sqrt:sqrtInf){
            assertTrue(String.format("sqrt(Infinity)!=Infinity%n." +
                "sqrt(Infinity)=%1$s",sqrt),inf.equals(sqrt));
        }
    }

    @Test
    public void testAllRootsOfInfinityPolar() throws Exception {
        /*
       sqrt(Infinity)=Infinity
        */
        PolarComplex inf = PolarComplex.COMPLEX_INFINITY;
        PolarComplex[] sqrtInf = ComplexMath.allRoots(inf,5);

        for(PolarComplex sqrt:sqrtInf){
            assertTrue(String.format("sqrt(Infinity)!=Infinity%n." +
                    "sqrt(Infinity)=%1$s",sqrt),inf.equals(sqrt));
        }
    }

/*-------------------------------------------------------------------*/



}
